//cSNESPointer.cpp
#include "cSNESPointer.h"

/* Default constructor.
 */
SNESPointer::SNESPointer()
{
	this->Mapping = LOROM;
	this->HeaderSize = 512;
	this->BankSize = 0x8000;
	this->SetSNESAddr(0, 0);
}

/* Constructor with file address.
 */
SNESPointer::SNESPointer(u32 FileAddr)
{
	this->Mapping = LOROM;
	this->HeaderSize = 512;
	this->BankSize = 0x8000;
	this->SetFileAddr(FileAddr);
}

/* Constructor with SNES address.
 */
SNESPointer::SNESPointer(u8 Bank, u16 Addr)
{
	this->Mapping = LOROM;
	this->HeaderSize = 512;
	this->BankSize = 0x8000;
	this->SetSNESAddr(Bank, Addr);
}

/* Destructor.
 */
SNESPointer::~SNESPointer()
{
}

/* Reads a pointer from a file.
 * Inputs:
 * -File: File to read from.
 * -Addr: Address to read from.
 * Returns: true on success, false on failure.
 */
bool SNESPointer::Read(ROMFile *File, u32 Addr)
{
	File->Seek(Addr);
	this->Addr = File->Read() | (File->Read() << 8);
	this->Bank = File->Read();
	return true;
}

/* Reads a 2-byte pointer from a file.
 * Inputs:
 * -File: File to read from.
 * -Addr: Address to read from.
 * Returns: true on success, false on failure.
 */
bool SNESPointer::Read2Byte(ROMFile *File, u32 Addr)
{
	File->Seek(Addr);
	this->Addr = File->Read() | (File->Read() << 8);
	return true;
}

bool SNESPointer::Read2Byte(ROMFile *File, u32 Addr, u8 Bank)
{
	this->Bank = Bank;
	return this->Read2Byte(File, Addr);
}

/* Assigns a file address.
 * Inputs:
 * -Addr: File address.
 */
void SNESPointer::SetFileAddr(u32 Addr)
{
	this->Bank = (((Addr - this->HeaderSize) & 0x7F8000) << 1);
	this->Addr = 0x8000 + ((Addr - this->HeaderSize) & 0x7FFF);
}

/* Assigns a SNES address.
 * Inputs:
 * -Bank, Addr: SNES address.
 */
void SNESPointer::SetSNESAddr(u8 Bank, u16 Addr)
{
	this->Bank = Bank;
	this->Addr = Addr;
}

/* Converts to a file address.
 * Returns: File address, or MAX_INT if the pointer does not point to ROM.
 */
u32 SNESPointer::ToFileAddr()
{
	if(this->Addr < 0x8000) return MAX_INT;
	else return ((this->Addr - (this->BankSize - this->HeaderSize))
		| (this->Bank * this->BankSize));
}

/* Converts to a SNES address.
 * Returns: SNES address (low 2 bytes address, 3rd byte bank).
 */
u32 SNESPointer::ToSNESAddr()
{
	return ((u32)this->Bank << 16) | (u32)this->Addr;
}

/* Converts to a RAM address.
 * Returns: RAM address, or MAX_INT if the pointer does not point to RAM.
 */
u32 SNESPointer::ToRAMAddr()
{
	if(this->Addr >= 0x8000) return MAX_INT;
	if((this->Bank == 0x7E) || (this->Bank == 0x7F))
		return ((u32)this->Bank << 16) | (u32)this->Addr;
	else return 0x7E0000 | (u32)this->Addr;
}

/* Addition operator.
 */
SNESPointer SNESPointer::operator+(const u16 Addr)
{
	SNESPointer P(this->Bank, this->Addr);
	u32 NewAddr = P.Addr + Addr;
	if((P.Addr < 0x8000) && (NewAddr >= 0x8000))
	{
		P.Addr = NewAddr & 0x7FFF;
		P.Bank++;
	}
	else if((P.Addr > 0x8000) && (NewAddr >= 0x10000))
	{
		P.Addr = (NewAddr & 0x7FFF) | 0x8000;
		P.Bank++;
	}
	else P.Addr = NewAddr;
	return P;
}

/* Addition assignment operator.
 */
void SNESPointer::operator+=(const u16 Addr)
{
	u32 NewAddr = this->Addr + Addr;
	if((this->Addr < 0x8000) && (NewAddr >= 0x8000))
	{
		this->Addr = NewAddr & 0x7FFF;
		this->Bank++;
	}
	else if((this->Addr > 0x8000) && (NewAddr >= 0x10000))
	{
		this->Addr = (NewAddr & 0x7FFF) | 0x8000;
		this->Bank++;
	}
	else this->Addr = NewAddr;
}

